/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package test.alan.hyperblobs;

import android.graphics.Color;
import android.opengl.GLES20;
import android.util.FloatMath;

import org.jbox2d.common.MathUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.*;

/**
 * A OpenGL Octagon that will represent our blob. <br/> Coordinates are based
 * off a 360x480 screen. (Landscape)
 *
 * @author Michael Zhang
 */
public class GLBlob {

    /**
     * Vertex Shader.
     */
    private static final String vertexShaderCode = ""
            + "uniform mat4 uMVPMatrix;"
            + "attribute vec4 vPosition;"
            + "void main() {"
            + "  gl_Position = vPosition * uMVPMatrix;"
            + "}";
    /**
     * Fragment Shader.
     */
    private static final String fragmentShaderCode = ""
            + "precision mediump float;"
            + "uniform vec4 vColor;"
            + "void main() {"
            + "  gl_FragColor = vColor;"
            + "}";
    /**
     * X, Y, Z
     */
    private static final int COORDS_PER_VERTEX = 3;
    /**
     * Total number of vertices.
     */
    private static final int VERTEX_COUNT = 8;
    /**
     * Bytes per vertex.
     */
    private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * 4;
    /**
     * The width of the emulated screen.
     */
    private static final float WIDTH = 480;
    /**
     * The height of the emulated screen.
     */
    private static final float HEIGHT = 360;
    /**
     * Factor to scale incoming y-coords by.
     */
    private static final float SCALE = 2 / HEIGHT;
    /**
     * The sine of 45 degrees. (Used by getVerticies)
     */
    private static final float SIN45 = FloatMath.sin(MathUtils.QUARTER_PI);
    /**
     * The cosine of 45 degrees. (Used by getVerticies)
     */
    private static final float COS45 = FloatMath.cos(MathUtils.QUARTER_PI);
    /**
     * The order in which the vertices should be drawn.
     */
    private static final short[] DRAW_ORDER = {
        0, 1, 2,
        0, 2, 3,
        0, 3, 4,
        0, 4, 5,
        0, 5, 6,
        0, 6, 7
    };
    /**
     * Buffer that will store our vertices.
     */
    private final FloatBuffer mVertexBuffer;
    /**
     * Buffer that will store our drawing order.
     */
    private final ShortBuffer mDrawListBuffer;
    /**
     * Our program containing our shaders.
     */
    private final int mProgram;
    /**
     * Our color.
     */
    private float[] mColor = new float[4];

    /**
     *
     */
    public GLBlob() {
        mVertexBuffer = ByteBuffer.
                allocateDirect(VERTEX_COUNT * COORDS_PER_VERTEX * 4).
                order(ByteOrder.nativeOrder()).
                asFloatBuffer();

        // make our OpenGL Program
        mProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(mProgram, LevelDrawer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode));
        GLES20.glAttachShader(mProgram, LevelDrawer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode));
        GLES20.glLinkProgram(mProgram);

        mDrawListBuffer = ByteBuffer.
                allocateDirect(DRAW_ORDER.length * 2).
                order(ByteOrder.nativeOrder()).
                asShortBuffer().
                put(DRAW_ORDER);
        mDrawListBuffer.position(0);

    }

    /**
     * Sets the color of this triangle.
     *
     * @param color The color as defined in
     */
    public void setColor(int color) {
        mColor[0] = (float) Color.red(color) / 0xFF; // divide by 255 to find the percentage of that color
        mColor[1] = (float) Color.green(color) / 0xFF; // which is what OpenGL uses for coloring
        mColor[2] = (float) Color.blue(color) / 0xFF;
        mColor[3] = (float) Color.alpha(color) / 0xFF;
    }

    /**
     * Draws this rectangle.
     *
     * @param mvpMatrix The matrix to draw with
     */
    public void draw(float[] mvpMatrix) {
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);
        // get handle to vertex shader's vPosition member
        int positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        // Enable a handle to the rectangle vertices
        GLES20.glEnableVertexAttribArray(positionHandle);
        // Prepare the rectangle coordinate data
        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, VERTEX_STRIDE, mVertexBuffer);
        // Get handle to fragment shader's vColor member
        int colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        // Set color for drawing the rectangle
        GLES20.glUniform4fv(colorHandle, 1, mColor, 0);
        // get handle to shape's transformation matrix
        int mvpMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);
        // Draw the rectangle
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, DRAW_ORDER.length, GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer);
        // Disable vertex array
        GLES20.glDisableVertexAttribArray(positionHandle);
    }

    /**
     *
     * @param x Center x coords
     * @param y Center y coords
     * @param r Radius
     */
    @SuppressWarnings(value = "AssignmentToMethodParameter")
    public final void setPosition(int x, int y, float r) {
        // OpenGL coordinate space has the origin at the center.
        // Our emulated screen's coorditate space has the
        // origin at the top-left. We need to convert those.
        x -= WIDTH / 2;
        y -= HEIGHT / 2;
        y = -y; // OpenGL coordinates have +y going up instead of down


        float[] coords = getVertices(x, y, r);

        System.out.println(Arrays.toString(coords));
        mVertexBuffer.position(0);
        mVertexBuffer.put(coords);
        mVertexBuffer.position(0);
    }

    /**
     * Returns an array with the coordinates of the vertices.
     *
     * @param x Center x coords
     * @param y Center y coords
     * @param r Half-Width
     * @return An array with our coords
     */
    private float[] getVertices(int x, int y, float r) {
        // initialize OpenGL coordinates
        float[] coords = new float[VERTEX_COUNT * COORDS_PER_VERTEX];

        // bounding box points
        // point 0 (top)
        coords[0] = (x) * SCALE; // X
        coords[1] = (y + r) * SCALE; // Y
        coords[2] = 0; // Z -- since this is 2d, z will always be 0

        // point 2 (right)
        coords[6] = (x + r) * SCALE;
        coords[7] = (y) * SCALE;
        coords[8] = 0;

        // point 4 (bottom)
        coords[12] = (x) * SCALE;
        coords[13] = (y - r) * SCALE;
        coords[14] = 0;

        // point 6 (left)
        coords[18] = (x - r) * SCALE;
        coords[19] = (y) * SCALE;
        coords[20] = 0;

        // inscribed square points
        float hWidth = r * COS45;
        float hHeight = r * SIN45;

        // point 1
        coords[3] = (x + hWidth) * SCALE;
        coords[4] = (y + hHeight) * SCALE;
        coords[5] = 0;

        // point 3
        coords[9] = (x + hWidth) * SCALE;
        coords[10] = (y - hHeight) * SCALE;
        coords[11] = 0;

        // point 5
        coords[15] = (x - hWidth) * SCALE;
        coords[16] = (y - hHeight) * SCALE;
        coords[17] = 0;

        // point 7
        coords[21] = (x - hWidth) * SCALE;
        coords[22] = (y + hHeight) * SCALE;
        coords[23] = 0;


        return coords;
    }
}
